﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Djs.Games.DeskGame.GameComponents;
using Djs.WinForm.Components;

namespace Djs.Games.DeskGame.GameDefinitions
{
    /// <summary>
    /// Třída, která představuje definici hry.
    /// Bázová třída je abstraktní, určuje vlastnosti, které definice musí mít, ale neobsahuje konkrétní definici hrací plochy ani pravidel.
    /// Konkrétní hru představuje potomek této třídy.
    /// </summary>
    public abstract class Game : IGame
    {
        #region Konstrukce
        public Game()
        {
            this.OuterBorder = 1f;
            this.Pines = new List<GamePine>();
            this.Cells = new List<GameCell>();
            this.Links = new List<GameLink>();
            this.Rands = new List<GameRand>();
        }
        #endregion
        #region ClassFactory
        /// <summary>
        /// Vrátí instanci třídy, která je potomkem GameDefinition odpovídajícím požadované hře.
        /// Podle jména hry určí konrétní třídu, vytvoří její instanci a vyvolá její metodu PrepareDefinition(gameName).
        /// </summary>
        /// <param name="gameName"></param>
        /// <returns></returns>
        public static Game GetGameDefinition(string gameName)
        {
            Game game = null;

            // Zeptáme se našich známých definic (ačkoliv generické zjištění potomků by bylo lepší), zda umí hrát tuto hru:
            if (LudoGame.ImplementsGame(gameName))
                game = new LudoGame();
            //else if (1 == 0)
            //    game = null;

            if (game != null)
            {
                game.GameName = gameName;
                game.PrepareDefinition(gameName);
                game.Reset();
            }
            return game;
        }
        /// <summary>
        /// Abstraktní metoda, volá se při tvorbě definice konkrétní hry, po jejím konstruktoru.
        /// Účel: naplnit konstantní prvky (typicky this.Cells).
        /// </summary>
        /// <param name="gameName"></param>
        public abstract void PrepareDefinition(string gameName);
        /// <summary>
        /// Virtuální metoda, volá se kdykoliv když je třeba hru vrátit do výchozí pozice.
        /// Nemaže se definice hry (Cells, Links).
        /// Potomek si má vrátit hru do stavu jako po rozdání.
        /// </summary>
        public virtual void Reset()
        {
        }
        /// <summary>
        /// true, pokud aktuální definice je platná.
        /// Bázová třída vrací true, pokud existují nějaká definovaná políčka.
        /// </summary>
        public virtual bool IsValid { get { return (this.Cells != null && this.Cells.Count > 0); } }
        #endregion
        #region Data a metody pro definici hracích polí a přepočty logických na vizuální souřadnice
        /// <summary>
        /// Název hry
        /// </summary>
        public string GameName { get; protected set; }
        /// <summary>
        /// Soupis všech hráčů. Ne všichni musí být aktivně ve hře, mohou stát mimo (třeba jedno kolo nehrají).
        /// Někteří mohou být 
        /// </summary>
        public List<GamePlayer> Players { get; private set; }
        /// <summary>
        /// Soupis všech figurek, nacházejících se na hrací ploše
        /// </summary>
        public List<GamePine> Pines { get; private set; }
        /// <summary>
        /// Soupis všech herních políček
        /// </summary>
        public List<GameCell> Cells { get; private set; }
        /// <summary>
        /// Soupis všech vztahů mezi buňkami
        /// </summary>
        public List<GameLink> Links { get; private set; }
        /// <summary>
        /// Soupis všech randomizerů (generátor náhodných hodnot, hrací kostka)
        /// </summary>
        public List<GameRand> Rands { get; private set; }
        /// <summary>
        /// Soupis všech aktuálně vykreslovaných políček (Cells, Links, Pines)
        /// </summary>
        public List<IItem> IItems { get; private set; }
        /// <summary>
        /// Velikost okrajů okolo nejkrajnějších buněk hrací plochy.
        /// </summary>
        public float OuterBorder { get; protected set; }
        /// <summary>
        /// Index posledního herního políčka (= this.Cells.Count - 1)
        /// </summary>
        public int LastCellIndex { get { return this.Cells.Count - 1; } }
        /// <summary>
        /// Poslední herní políčko (nebo null, když LastCellIndex == 0)
        /// </summary>
        public GameCell LastCell { get { return (this.Cells.Count == 0 ? null : this.Cells[this.Cells.Count - 1]); } }
        /// <summary>
        /// Souřadnice všech buněk + okraje, logické
        /// </summary>
        internal RectangleF CellLogicalBound { get; private set; }
        /// <summary>
        /// Souřadnice všech buněk + okraje, vizuální, v pixelech
        /// </summary>
        internal RectangleF CellVisualBound { get; private set; }
        /// <summary>
        /// Přidá do seznamu dalšího hráče
        /// </summary>
        /// <param name="player"></param>
        public void AddPlayer(GamePlayer player)
        {
            if (player != null)
            {
                player.Owner = this;
                this.Players.Add(player);
                this.SyncPlayerContent(player);
            }
        }
        /// <summary>
        /// Přidá do seznamu další figurku
        /// </summary>
        /// <param name="pine"></param>
        public void AddPine(GamePine pine)
        {
            if (pine != null)
            {
                pine.Owner = this;
                this.Pines.Add(pine);
                this.SyncPlayerContent(pine.Player);
            }
        }
        /// <summary>
        /// Přidá do seznamu další políčko
        /// </summary>
        /// <param name="cell"></param>
        public void AddCell(GameCell cell)
        {
            if (cell != null)
            {
                cell.Owner = this;
                this.Cells.Add(cell);
                this.SyncPlayerContent(cell.Player);
            }
        }
        /// <summary>
        /// Přidá do seznamu další vztah
        /// </summary>
        /// <param name="link"></param>
        public void AddLink(GameLink link)
        {
            if (link != null)
            {
                link.Owner = this;
                this.Links.Add(link);
            }
        }
        /// <summary>
        /// Přidá do seznamu další randomizer
        /// </summary>
        /// <param name="rand"></param>
        public void AddRand(GameRand rand)
        {
            if (rand != null)
            {
                rand.Owner = this;
                this.Rands.Add(rand);
            }
        }
        /// <summary>
        /// Zajistí, že pro hráče "player" budou v jeho seznamech uloženy všechny jeho objekty (Pine, Cell, Rand)
        /// </summary>
        /// <param name="player"></param>
        public virtual void SyncPlayerContent(GamePlayer player)
        {
        }

        #endregion
        #region Přepočty souřadnic logických na vizuální a zpět
        /// <summary>
        /// Metoda zajistí přípravu koeficientů pro přepočty logických na vizuální souřadnice a reverzní
        /// </summary>
        /// <param name="visualArea"></param>
        protected void TransformPrepare(Rectangle visualArea)
        {
            // Je zapotřebí provádět přepočty? A je to možné?
            if (!this.IsValid)
            {
                this.TransformBound = null;
                return;
            }
            if (this.TransformBound != null && this.TransformBound.VisualArea == visualArea)
                return;

            // Sumarizovat LogicalBounds ze všech Cells, a přidat okraje OuterBorder:
            bool cellFirst = true;
            RectangleF logicalArea  = RectangleF.Empty;
            foreach (GameCell cell in this.Cells)
            {
                RectangleF logicalItem = cell.LogicalBounds;
                if (cellFirst)
                {
                    logicalArea = logicalItem;
                    cellFirst = false;
                }
                else
                    logicalArea = RectangleF.Union(logicalArea, logicalItem);
            }
            this.CellLogicalBound = logicalArea.Enlarge(this.OuterBorder);

            // Sestavit objekt transformací:
            this.TransformBound = new TransformBound(this.CellLogicalBound, visualArea);

            // Transformovat sumární souřadnice políček:
            this.CellVisualBound = this.TransformBound.TransformLogicalToVisual(this.CellLogicalBound);
        }
        /// <summary>
        /// Aktuální objekt pro transformaci souřadnic
        /// </summary>
        protected TransformBound TransformBound { get; set; }
        #endregion
        #region Kreslení plochy
        /// <summary>
        /// Vykreslí vše na pracovní plochu editoru.
        /// Zajistí i přepočty transformačních parametrů.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <param name="visualArea"></param>
        internal void Paint(GameArea gameArea, LayeredPaintEventArgs e, Rectangle visualArea)
        {
            if (e.HasPaintLayer(0))
            {   // Příprava položek (transformace, výpočty souřadnic Visual ze souřadnic Logical) a vykreslení pozadí se provádí pouze při kreslení vrstvy 0:
                this.PrepareIItems(visualArea);
                this.PaintBackground(gameArea, e);
            }

            // Kreslení prvků na základě vrstev:
            this.PaintIItems(gameArea, e);
        }
        /// <summary>
        /// Připraví do jednotlivých políček jejich aktuální souřadnice, 
        /// a to tak, aby se na fyzickou vizuální plochu vešly zobrazit všechny hrací políčka.
        /// </summary>
        /// <param name="visualArea"></param>
        internal void PrepareIItems(Rectangle visualArea)
        {
            // Určíme transformační parametry mezi souřadnicemi logickými a souřadnicemi vizuálními
            this.TransformPrepare(visualArea);

            // Připravím seznam, do kterého budu dávat všechny vizuální interaktivní prvky:
            List<IItem> iItems = new List<IItem>();

            // Do soupisu postupně vložíme položky: "odspodu" (link - cell - pine - rand), pokud se chtějí nechat vykreslit:
            foreach (GameLink link in this.Links)
            {
                link.TransformLogicalToVisual(this.TransformBound); // VisualWidth = this.TransformLogicalToVisual(link.LogicalWidth);
                IItem iItem = link;
                iItems.Add(iItem);
            }
            foreach (GameCell cell in this.Cells)
            {
                cell.TransformLogicalToVisual(this.TransformBound);
                IItem iItem = cell;
                iItems.Add(iItem);
            }
            foreach (GamePine pine in this.Pines)
            {
                pine.TransformLogicalToVisual(this.TransformBound);
                IItem iItem = pine;
                iItems.Add(iItem);
            }
            foreach (GameRand rand in this.Rands)
            {
                rand.TransformLogicalToVisual(this.TransformBound);
                IItem iItem = rand;
                iItems.Add(iItem);
            }

            this.IItems = iItems;
        }
        /// <summary>
        /// Vykreslí pozadí plochy
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void PaintBackground(GameArea gameArea, LayeredPaintEventArgs e)
        {
            Graphics g = e.GraphicsForLayer(0);
            RectangleF bound = this.CellVisualBound;
            float l = bound.Left;
            float t = bound.Top;
            float r = bound.Right - 1f;
            float b = bound.Bottom - 1f;
            using (Pen pen = new Pen(Color.LightCoral))
            {
                g.DrawLine(pen, l, t, r, t);
                g.DrawLine(pen, r, t, r, b);
                g.DrawLine(pen, r, b, l, b);
                g.DrawLine(pen, l, b, l, t);
            }
        }
        /// <summary>
        /// Vykreslí všechny prvky plochy (Links, Cells, Pines)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void PaintIItems(GameArea gameArea, LayeredPaintEventArgs e)
        {
            // Rozčlením jednotlivé IItem do skupin (0 - 3) po vrstvách:
            List<IItem>[] itemLayers = new List<IItem>[4];                                                // Pokud některá vrstva nebude obsahovat žádný prvek, bude zde pro ni [layer] = null.
            IEnumerable<IGrouping<int, IItem>> groups = this.IItems.GroupBy(iItem => iItem.VisualLayer);  // Sestavím grupy prvků podle jejich vrstvy
            foreach (IGrouping<int, IItem> group in groups)
            {   // Typicky budou 3 až 4 průchody:
                int visualLayer = group.Key;
                if (visualLayer >= 0 && visualLayer < 4)
                    itemLayers[visualLayer] = group.ToList();
            }

            // Budu kreslit prvky do jednotlivých vrstev, počínaje odspodu:
            bool draw = false;
            for (int layer = 0; layer < 4; layer++)
            {
                // Budu kreslit počínaje tou vrstvou, která se má první kreslit (+ budu kreslit všechny následující):
                if (e.HasPaintLayer(layer))
                    draw = true;

                if (draw)
                {   // Tuto vrstvu mám kreslit:
                    List<IItem> items = itemLayers[layer];

                    // Přenesu do této vrstvy obsah vrstvy předešlé (ten by měl obsahovat sumu předchozích nižších vrstev):
                    // A to a) pokud tato vrstva není vrstvou 0 (pak není odkud přenášet),
                    //    a b) pokud tato vrstva je potřebná (to je když obsahuje nějaké prvky, anebo pokud jde o vrstvu nižší než poslední).
                    if (layer > 0 && (items != null || layer < 3))
                        e.CopyContentOfLayer(layer - 1, layer);

                    // Vykreslím všechny položky:
                    if (items != null)
                        this.PaintIItemsForLayer(gameArea, e, layer, items);
                }
            }
        }
        /// <summary>
        /// Vykreslí obsah prvků jedné grafické vrstvy.
        /// </summary>
        /// <param name="gameArea"></param>
        /// <param name="e"></param>
        /// <param name="layer"></param>
        /// <param name="items"></param>
        protected void PaintIItemsForLayer(GameArea gameArea, LayeredPaintEventArgs e, int layer, List<IItem> items)
        {
            Graphics graphics = e.GraphicsForLayer(layer);
            var gState = graphics.Save();

            foreach (IItem iItem in items)
            {
                if (iItem.EnableAction(ActionType.Paint))
                    iItem.Paint(gameArea, graphics);
            }

            graphics.Restore(gState);
        }
        /// <summary>
        /// Knihovna obrázků. Může být sdílená s jinými objekty.
        /// Je autoinicializační (nikdy není null).
        /// </summary>
        public GameImageLibrary ImageLibrary
        {
            get 
            {
                if (this._ImageLibrary == null)
                    this._ImageLibrary = new GameImageLibrary();
                return this._ImageLibrary;
            }
            set { this._ImageLibrary = value; }
        }
        private GameImageLibrary _ImageLibrary;
        #endregion
        #region Podpora pro animaci
        /// <summary>
        /// Událost, kdy hra sama chce překreslit určitou vrstvu.
        /// Typicky při animaci.
        /// </summary>
        public event ReDrawLayerEventHandler ReDrawLayer;
        /// <summary>
        /// Vyvolá eventhandler ReDrawLayer
        /// </summary>
        /// <param name="layer"></param>
        protected virtual void OnReDrawLayer(int layer)
        {
            if (this.ReDrawLayer != null)
                this.ReDrawLayer(this, new ReDrawLayerEventArgs(layer));
        }
        /// <summary>
        /// Zahájí animaci daného prvku s danou cestou.
        /// </summary>
        /// <param name="iItem"></param>
        /// <param name="pathPoints"></param>
        public virtual void RunAnimation(IItem iItem, IEnumerable<GameAnimatorPathPoint> pathPoints)
        {
            this.GameAnimator = new GameAnimator(iItem, pathPoints, this.OnReDrawLayer);
            this.GameAnimator.Start();
        }
        /// <summary>
        /// Objekt animátoru
        /// </summary>
        protected GameAnimator GameAnimator { get; set; }
        #endregion
        #region Interaktivita
        /// <summary>
        /// Vrátí prvek, který leží na daném pointu.
        /// Pokud jich tam leží víc, vrací ten nejvyšší v ZOrder.
        /// Detekci prvku provádí sám prvek v jeho metodě IItem.IsOnPoint(), 
        /// takže prvky mohou reagovat na svůj specifický tvar (elipse, triangle, graphicpath), nikoli jen na rectangle.
        /// </summary>
        /// <param name="visualPoint">Testovaný bod, kde se nachází myš</param>
        /// <returns></returns>
        public IItem FindItemOnPoint(PointF visualPoint)
        {
            return this.FindItemOnPoint(visualPoint, ActionType.None);
        }
        /// <summary>
        /// Vrátí prvek, který leží na daném pointu.
        /// Pokud jich tam leží víc, vrací ten nejvyšší v ZOrder.
        /// Detekci prvku provádí sám prvek v jeho metodě IItem.IsOnPoint(), 
        /// takže prvky mohou reagovat na svůj specifický tvar (elipse, triangle, graphicpath), nikoli jen na rectangle.
        /// </summary>
        /// <param name="visualPoint">Testovaný bod, kde se nachází myš</param>
        /// <param name="enabledAction">Hledat jen takový prvek, který povoluje danou akci. Pokud je předána akce None, hledá jakýkoli prvek.</param>
        /// <returns></returns>
        public IItem FindItemOnPoint(PointF visualPoint, ActionType enabledAction)
        {
            if (enabledAction == ActionType.None)
                return this.IItems.FindLast(item => item.IsOnPoint(visualPoint));
            else
                return this.IItems.FindLast(item => item.IsOnPoint(visualPoint) && item.EnableAction(enabledAction));
        }
        /// <summary>
        /// Zjistí, zda daná akce je povolená pro herní plochu (nikoli pro prvek, ten se testuje adresně).
        /// Zatím se volá jen pro MoveBegin
        /// </summary>
        /// <param name="actionType"></param>
        /// <returns></returns>
        public bool EnableAction(ActionType actionType)
        {
            switch (actionType)
            {
                case ActionType.MoveBegin:
                    return false;
            }
            return false;
        }
        #endregion
        #region Podpora pro hromadné vytváření vztahů
        /// <summary>
        /// Vytvoří standardní linky mezi sousedními prvky od prvního do posledního, a prolinkuje poslední do prvního.
        /// </summary>
        public void CreateLinkChain()
        {
            this._CreateLinkChain(0, this.Cells.Count - 1, true, _CreateLinkDefault);
        }
        /// <summary>
        /// Vytvoří standardní linky mezi sousedními prvky od prvního do posledního, a podle parametru bool setCycle prolinkuje poslední do prvního.
        /// </summary>
        /// <param name="setCycle">Zacyklovat = spojit poslední políčko do prvního</param>
        public void CreateLinkChain(bool setCycle)
        {
            this._CreateLinkChain(0, this.Cells.Count - 1, setCycle, _CreateLinkDefault);
        }
        /// <summary>
        /// Vytvoří standardní linky mezi sousedními prvky od prvního do posledního, a podle parametru bool setCycle prolinkuje poslední do prvního.
        /// </summary>
        public void CreateLinkChain(bool setCycle, bool drawLink)
        {
            this._CreateLinkChain(0, this.Cells.Count - 1, setCycle, (c1, c2) => new GameLink() { Cell1 = c1, Cell2 = c2, DrawLink = drawLink });
        }
        /// <summary>
        /// Vytvoří linky mezi sousedními prvky od prvního do posledního, a prolinkuje poslední do prvního.
        /// </summary>
        /// <param name="linkCreator">Funkce, která vytvoří link ze dvou buněk</param>
        public void CreateLinkChain(Func<GameCell, GameCell, GameLink> linkCreator)
        {
            this._CreateLinkChain(0, this.Cells.Count - 1, true, linkCreator);
        }
        /// <summary>
        /// Vytvoří linky mezi sousedními prvky od prvního do posledního, a prolinkuje poslední do prvního.
        /// </summary>
        /// <param name="setCycle">Zacyklovat = spojit poslední políčko do prvního</param>
        /// <param name="linkCreator">Funkce, která vytvoří link ze dvou buněk</param>
        public void CreateLinkChain(bool setCycle, Func<GameCell, GameCell, GameLink> linkCreator)
        {
            this._CreateLinkChain(0, this.Cells.Count - 1, setCycle, linkCreator);
        }
        /// <summary>
        /// Vytvoří standardní linky mezi explicitně danými prvky.
        /// </summary>
        /// <param name="indexes">Indexy prvků, které se mají spojit. Nepropojuje se poslední s prvním (ale index z prvního místa se může uvést i na posledním místě, pak se Link spojí).</param>
        public void CreateLinkChain(params int[] indexes)
        {
            this._CreateLinkChain(indexes, _CreateLinkDefault);
        }
        /// <summary>
        /// Vytvoří linky mezi explicitně danými prvky
        /// </summary>
        /// <param name="linkCreator">Funkce, která vytvoří link ze dvou buněk</param>
        /// <param name="indexes">Indexy prvků, které se mají spojit. Nepropojuje se poslední s prvním (ale index z prvního místa se může uvést i na posledním místě, pak se Link spojí).</param>
        public void CreateLinkChain(Func<GameCell, GameCell, GameLink> linkCreator, params int[] indexes)
        {
            this._CreateLinkChain(indexes, linkCreator);
        }
        /// <summary>
        /// Vytvoří linky mezi explicitně danými prvky
        /// </summary>
        /// <param name="linkCreator">Funkce, která vytvoří link ze dvou buněk</param>
        /// <param name="indexes">Indexy prvků, které se mají spojit. Nepropojuje se poslední s prvním (ale index z prvního místa se může uvést i na posledním místě, pak se Link spojí).</param>
        public void CreateLinkChain(Func<GameCell, GameCell, GameLink> linkCreator, IEnumerable<int> indexes)
        {
            this._CreateLinkChain(indexes.ToArray(), linkCreator);
        }
        /// <summary>
        /// Implicitní creator pro GameLink
        /// </summary>
        /// <param name="cell1"></param>
        /// <param name="cell2"></param>
        /// <returns></returns>
        private GameLink _CreateLinkDefault(GameCell cell1, GameCell cell2)
        {
            return new GameLink() { Cell1 = cell1, Cell2 = cell2, DrawLink = true };
        }
        /// <summary>
        /// Privátní metoda pro vytvoření sady linků z řady políček, daných indexem From a To
        /// </summary>
        /// <param name="fromIndex"></param>
        /// <param name="toIndex"></param>
        /// <param name="setCycle"></param>
        /// <param name="linkCreator"></param>
        private void _CreateLinkChain(int fromIndex, int toIndex, bool setCycle, Func<GameCell, GameCell, GameLink> linkCreator)
        {
            int cnt = this.Cells.Count;
            int f = (fromIndex < 0 ? 0 : (fromIndex >= cnt ? (cnt - 1) : fromIndex));
            int t = (toIndex < f ? f : (toIndex >= cnt ? (cnt - 1) : toIndex));
            for (int i = f; i <= t; i++)
            {
                // Pokud i je na pozici posledního políčka, a nemáme dělat kruh, skončíme:
                if (i == t && !setCycle)
                    break;

                if (i >= 0 && i < cnt)
                {
                    int j = (i < t ? i + 1 : f);          // Index políčka 2: pokud i je poslední políčko, pak j bude první políčko
                    GameCell cell1 = this.Cells[i];
                    GameCell cell2 = this.Cells[j];
                    GameLink link = linkCreator(cell1, cell2);
                    this.AddLink(link);
                }
            }
        }
        /// <summary>
        /// Privátní metoda pro vytvoření sady linků z řady políček, daných explicitními indexy
        /// </summary>
        /// <param name="indexes"></param>
        /// <param name="linkCreator"></param>
        private void _CreateLinkChain(int[] indexes, Func<GameCell, GameCell, GameLink> linkCreator)
        {
            for (int i = 0; i < (indexes.Length - 1); i++)
            {
                int c1 = indexes[i];
                int c2 = indexes[i + 1];
                GameLink link = linkCreator(this.Cells[c1], this.Cells[c2]);
                this.AddLink(link);
            }
        }
        #endregion
        #region Vyhledání dat
        /// <summary>
        /// Najde a vrátí políčko (Cell), které se nachází na daném vizuálním bodě.
        /// Pokud nenajde, vrátí null.
        /// </summary>
        /// <param name="visualCenter"></param>
        /// <returns></returns>
        public virtual GameCell FindCellOnVisualPoint(PointF visualCenter)
        {
            return this.Cells.FirstOrDefault(c => c.IsOnPoint(visualCenter));            
        }
        /// <summary>
        /// Najde a vrátí hráče daného čísla
        /// </summary>
        /// <param name="player"></param>
        /// <returns></returns>
        public virtual GamePlayer FindPlayer(int player)
        {
            return this.Players.FirstOrDefault(p => p.Player == player);
        }
        /// <summary>
        /// Najde a vrátí všechny vztahy (GameLink) z daného políčka.
        /// Pokud není zadané políčko, vrátí null.
        /// </summary>
        /// <param name="cell"></param>
        /// <returns></returns>
        public virtual List<GameLink> FindLinkOnCell(GameCell cell)
        {
            if (cell == null) return null;
            return this.Links.FindAll(link => (Object.ReferenceEquals(link.Cell1, cell) || Object.ReferenceEquals(link.Cell2, cell)));
        }
        /// <summary>
        /// Najde a vrátí všechny vztahy (GameLink) z daného políčka daným směrem.
        /// Pokud není zadané políčko, vrátí null.
        /// Pro směr Next hledá vztahy, kde dané políčko cell je ve vztahu v property Cell1 (výchozí).
        /// Pro směr Prev hledá vztahy, kde dané políčko cell je ve vztahu v property Cell2 (cílové).
        /// </summary>
        /// <param name="cell"></param>
        /// <returns></returns>
        public virtual List<GameLink> FindLinkOnCell(GameCell cell, GameMoveDirection direction)
        {
            if (cell == null) return null;
            switch (direction)
            {
                case GameMoveDirection.Next:
                    return this.Links.FindAll(link => (Object.ReferenceEquals(link.Cell1, cell)));
                case GameMoveDirection.Prev:
                    return this.Links.FindAll(link => (Object.ReferenceEquals(link.Cell2, cell)));
            }
            return new List<GameLink>();
        }
        /// <summary>
        /// Najde a vrátí všechna cílová políčka (GameCell), která jsou přímo linkovaná s výchozím políčkem, v daném směru.
        /// Pokud není zadané políčko, vrátí null.
        /// Pro směr Next hledá vztahy, kde dané políčko cell je ve vztahu v property Cell1 (výchozí).
        /// Pro směr Prev hledá vztahy, kde dané políčko cell je ve vztahu v property Cell2 (cílové).
        /// </summary>
        /// <param name="cell"></param>
        /// <returns></returns>
        public virtual List<GameCell> FindCellsLinkedToCell(GameCell cell, GameMoveDirection direction)
        {
            // Najde vztahy v daném směru:
            List<GameLink> links = this.FindLinkOnCell(cell, direction);
            if (links == null) return null;

            // Uloží políčka ze vztahů v daném směru:
            List<GameCell> cells = new List<GameCell>();
            foreach (GameLink link in links)
            {
                GameCell linkCell = link.GetCell(direction);
                if (linkCell != null)
                    cells.Add(linkCell);
            }
            return cells;
        }

        /// <summary>
        /// Najde a vrátí všechny figurky, které se nachází na daném políčku
        /// (jejichž GamePine.Cell je shodný s daným objektem).
        /// Pokud je předáno políčko cell == null, vrací null.
        /// </summary>
        /// <param name="cell"></param>
        /// <returns></returns>
        public virtual List<GamePine> FindPinesOnCell(GameCell cell)
        {
            if (cell == null) return null;
            return this.Pines.FindAll(pine => Object.ReferenceEquals(pine.Cell, cell));
        }
        #endregion
        #region IGame Members
        bool IGame.IsValid { get { return this.IsValid; } }
        bool IGame.EnableAction(ActionType action)
        {
            return this.EnableAction(action);
        }
        IItem IGame.FindItemOnPoint(PointF visualPoint, ActionType enabledAction)
        {
            return this.FindItemOnPoint(visualPoint, enabledAction);
        }
        void IGame.Paint(GameArea gameArea, LayeredPaintEventArgs e, Rectangle visualArea)
        {
            this.Paint(gameArea, e, visualArea);
        }

        #endregion
    }
    #region class GamePlayer : Třída, která představuje jednoho hráče
    /// <summary>
    /// GamePlayer : Třída, která představuje jednoho hráče.
    /// Hráč se nevykresluje, ale zahrnuje do sebe svoje soukromá políčka a svoje figurky.
    /// Hráč má různé stavové příznaky.
    /// Potomek hráče implementuje část herní logiky.
    /// </summary>
    public class GamePlayer
    {
        #region Konstrukce
        public GamePlayer()
        {
            this.IsActive = false;
            this.IsAlien = false;
            this.IsHuman = false;
            this.Cells = new List<GameCell>();
            this.Pines = new List<GamePine>();
        }
        #endregion
        #region Vlastník
        /// <summary>
        /// Vlastník = hrací plocha.
        /// Skrze vlastníka může každé políčko dohledávat své sousedy.
        /// </summary>
        public Game Owner { get; set; }
        #endregion
        #region Specifické property
        /// <summary>
        /// Index hráče, koresponduje s hrací plochou
        /// </summary>
        public int Player { get; protected set; }
        /// <summary>
        /// Pořadí hráče, v něm se střídají ve hře
        /// </summary>
        public int Order { get; set; }
        /// <summary>
        /// Soupis soukromých políček
        /// </summary>
        public List<GameCell> Cells { get; protected set; }
        /// <summary>
        /// Soupis figurek hráče
        /// </summary>
        public List<GamePine> Pines { get; protected set; }
        #endregion
        #region Stavové property, možno overridovat a odvodit i řízení dalších vlastností
        public virtual bool IsActive { get; set; }
        public virtual bool IsHuman { get; set; }
        public virtual bool IsAlien { get; set; }
        public virtual bool IsMovePine { get; set; }
        #endregion
    }
    #endregion
    #region class GamePine : Třída, která popisuje jednu figurku
    /// <summary>
    /// GamePine : Třída, která popisuje jednu figurku
    /// </summary>
    public class GamePine : GameItem
    {
        #region Konstrukce
        public GamePine()
        {
            this.DragEnable = true;
            this.AnimatedReturnEnable = true;
        }
        #endregion
        #region Specifické property
        /// <summary>
        /// Vztah na hráče, který figurku vlastní.
        /// Základní logika počítá s tím, že jednu figurku vlastní jen jeden hráč.
        /// </summary>
        public GamePlayer Player { get; set; }
        /// <summary>
        /// Vztah na buňku hrací plochy, kde se figurka právě nyní nachází.
        /// Základní logika může umístit jednu figurku na jedno nebo žádné políčko (null).
        /// </summary>
        public GameCell Cell { get; set; }
        /// <summary>
        /// Explicitní vizuální souřadnice.
        /// Pokud je figurka na políčku, je zde null 
        /// (a vizuální souřadnice se přebírají ze souřadnic políčka this.Cell, viz this.CurrentVisualBounds).
        /// Pokud je figurka mimo políčko (přetahuje se, cestuje automaticky), 
        /// pak je aktuální souřadnice uložena zde, a this.CurrentVisualBounds ji odsud přebírá.
        /// </summary>
        public RectangleF? ExplicitVisualBounds { get; set; }
        /// <summary>
        /// Název obrázku v situaci, kdy je figurka v klidu - mimo dosah myši
        /// </summary>
        public virtual string ImageNameStandard { get; set; }
        /// <summary>
        /// Název obrázku v situaci, kdy je figurka pod myší
        /// </summary>
        public virtual string ImageNameOnMouse { get; set; }
        /// <summary>
        /// Název obrázku v situaci, kdy je figurka stisknutá myší
        /// </summary>
        public virtual string ImageNameMouseDown { get; set; }
        /// <summary>
        /// Název obrázku v situaci, kdy se figurka pohybuje,
        /// a je mimo jakékoliv políčko.
        /// </summary>
        public virtual string ImageNameMoveOutOfCell { get; set; }
        /// <summary>
        /// Název obrázku v situaci, kdy se figurka pohybuje,
        /// a je umístěna nad políčkem, kam se smí upustit (OK).
        /// </summary>
        public virtual string ImageNameMoveCellOk { get; set; }
        /// <summary>
        /// Název obrázku v situaci, kdy se figurka pohybuje,
        /// a je umístěna nad políčkem, kam se sice smí upustit, ale bude to mít nějaký následek (Warning).
        /// </summary>
        public virtual string ImageNameMoveCellWarning { get; set; }
        /// <summary>
        /// Název obrázku v situaci, kdy se figurka pohybuje,
        /// a je umístěna nad políčkem, kam se nesmí upustit (Error).
        /// </summary>
        public virtual string ImageNameMoveCellError { get; set; }
        #endregion
        #region Podpora pro přemisťování figurky
        protected override bool EnableAction(ActionType action)
        {
            switch (action)
            {
                case ActionType.MouseEnter:
                    return this.PineMouseIsEnabled();
                case ActionType.MoveBegin:
                    return this.PineMoveIsEnabled();
                default:
                    return base.EnableAction(action);
            }
        }
        protected override bool RunAction(ActionType action, PointF visualCenter)
        {
            GameCell targetCell = null;
            bool reDraw = base.RunAction(action, visualCenter);   // Base metoda zajistí (i pro námi ošetřované stavy) řízení hodnot this.CurrentMouseState a VisualCenter.
            switch (action)
            {
                case ActionType.MouseEnter:
                    reDraw = this.PineMouseEnter(visualCenter);
                    break;
                case ActionType.MouseLeave:
                    reDraw = this.PineMouseLeave(visualCenter);
                    break;
                case ActionType.MoveBegin:
                    reDraw = this.PineMoveBegin(visualCenter);
                    break;
                case ActionType.MoveRun:
                    reDraw = this.PineMoveRun(visualCenter);
                    return reDraw;
                case ActionType.MoveEnd:
                    reDraw = this.PineMoveEnd(visualCenter);
                    return reDraw;
                case ActionType.AnimationBegin:
                    return reDraw;
                case ActionType.AnimationRun:
                    reDraw = this.PineAnimationRun(visualCenter);
                    return reDraw;
                case ActionType.AnimationEnd:
                    reDraw = this.PineAnimationEnd(visualCenter);
                    return reDraw;
            }
            return reDraw;
        }
        #endregion
        #region Přemisťování a interaktivita figurky - protected virtual pro potomstvo
        /// <summary>
        /// Zjistí, zda daná figurka je v aktuálním stavu interaktivní (reaguje na pohyb myši).
        /// base metoda vrací true, bez podmínek.
        /// </summary>
        /// <returns></returns>
        protected virtual bool PineMouseIsEnabled()
        {
            return true;
        }
        /// <summary>
        /// Zjistí, zda danou figurkou je možno v aktuálním stavu hry pohybovat.
        /// base metoda vrací true, bez podmínek.
        /// Poté, co se vrátí true, bude volána jedenkrát metoda this.PineMoveBegin(),
        /// a následně opakovaně při pohybu bude volána metoda this.PineMove???().
        /// </summary>
        /// <returns></returns>
        protected virtual bool PineMoveIsEnabled()
        {
            return true;
        }
        /// <summary>
        /// Metoda provede akci při vstupu myši nad figurku.
        /// To, že figurka povoluje aktivitu MouseEnter je ověřeno (metoda thisPineMouseIsEnabled() vrátila true).
        /// base metoda nastaví obrázek this.BackStdImageName = this.ImageNameOnMouse a vrátí true.
        /// </summary>
        /// <returns></returns>
        protected virtual bool PineMouseEnter(PointF visualCenter)
        {
            this.BackStdImageName = this.ImageNameOnMouse;
            return true;
        }
        /// <summary>
        /// Metoda provede akci při odchodu myši z figurky.
        /// base metoda nastaví obrázek this.BackStdImageName = this.ImageNameStandard a vrátí true.
        /// </summary>
        /// <returns></returns>
        protected virtual bool PineMouseLeave(PointF visualCenter)
        {
            this.BackStdImageName = this.ImageNameStandard;
            return true;
        }
        /// <summary>
        /// Figurka se začíná pohybovat.
        /// Může na to reagovat změnou vzhledu.
        /// base metoda nastaví obrázek this.BackStdImageName = this.ImageNameMoveCellOk a vrátí true.
        /// </summary>
        /// <param name="visualCenter"></param>
        /// <returns></returns>
        protected virtual bool PineMoveBegin(PointF visualCenter)
        {
            this.BackStdImageName = this.ImageNameMoveCellOk;
            return true;
        }
        /// <summary>
        /// Figurka se pohybuje.
        /// Může na to reagovat změnou vzhledu, i podle toho, nad jakým políčkem se pohybuje.
        /// base metoda dohledá pomocí metody this.this.PinMoveDetectResult() políčko, nad kterým se pohybuje, 
        /// dohledá figurky, které na něm stojí, a za pomoci virtuální metody this.PinMoveGetResult() určí vztah pohybu k poli.
        /// Následně určí přiměřený obrázek do this.BackStdImageName.
        /// </summary>
        /// <param name="visualCenter"></param>
        /// <returns></returns>
        protected virtual bool PineMoveRun(PointF visualCenter)
        {
            GameMoveResultType moveResult = this.PineMoveDetectResult(visualCenter);
            return this.PineMoveSetImage(moveResult);
        }
        /// <summary>
        /// Figurka se pohybovala, a myš se právě nyní zvedla = končí pohyb.
        /// Zde budeme umísťovat figurku do políčka = jádro hry.
        /// </summary>
        /// <param name="visualCenter"></param>
        /// <returns></returns>
        protected virtual bool PineMoveEnd(PointF visualCenter)
        {
            GameCell cell = this.Owner.FindCellOnVisualPoint(visualCenter);
            List<GamePine> pines = this.Owner.FindPinesOnCell(cell);
            GameMoveResultType moveResult = this.PineMoveGetResult(cell, pines);
            bool result = this.PineMoveFinalize(visualCenter, moveResult, cell, pines);
            return result;
        }
        /// <summary>
        /// Metoda dohledá políčko, nad nímž figurka stojí.
        /// Dohledá figurky, které na tomto políčku stojí.
        /// Nalezené výsledky předá do své virtuální metody this.PinMoveGetResult(), 
        /// která na tomto základě vrátí vztah pohybu k poli.
        /// </summary>
        /// <param name="visualCenter"></param>
        /// <returns></returns>
        protected virtual GameMoveResultType PineMoveDetectResult(PointF visualCenter)
        {
            GameCell cell = this.Owner.FindCellOnVisualPoint(visualCenter);
            List<GamePine> pines = this.Owner.FindPinesOnCell(cell);
            return this.PineMoveGetResult(cell, pines);
        }
        /// <summary>
        /// Metoda určí vztah figurky this při pohybu nad plochou,
        /// když se nachází nad daným políčkem (cell), a na tomto políčku se nacházejí určité figurky (pines).
        /// base metoda vrací implicitní výsledky.
        /// </summary>
        /// <param name="cell"></param>
        /// <param name="pines"></param>
        /// <returns></returns>
        protected virtual GameMoveResultType PineMoveGetResult(GameCell cell, List<GamePine> pines)
        {
            if (cell == null) return GameMoveResultType.OutOfCell;                            // Není žádné políčko
            if (pines == null || pines.Count == 0) return GameMoveResultType.CellOk;          // Je políčko a je prázdné
            if (pines.Any(p => p.Player == this.Player)) return GameMoveResultType.CellError; // Je políčko, a na něm je naše figurka
            return GameMoveResultType.CellWarning;                                            // Na políčku jsou cizí figurky
        }
        /// <summary>
        /// Metoda má za úkol nastavit obrázek (a celkový vzhled) figurky podle jejího aktuálního stavu.
        /// Aktuální stav dostává v parametru, stav zjistila metoda this.PineMoveDetectResult().
        /// Tuto metodu volá metoda this.PineMoveEnd().
        /// Bázová metoda uloží do this.BackStdImageName (název obrázku této figurky) hodnotu, kterou jí vrátí metoda this.PineMoveGetImage().
        /// Potomek může overridovat, reagovat více způsoby...
        /// </summary>
        /// <param name="moveResult"></param>
        /// <returns></returns>
        protected virtual bool PineMoveSetImage(GameMoveResultType moveResult)
        {
            this.BackStdImageName = this.PineMoveGetImage(moveResult);
            return true;
        }
        /// <summary>
        /// Metoda vrátí obrázek pro aktuální stav figurky.
        /// Metoda se volá z metody this.PineMoveSetImage(), která se volá z metody this.PineMoveRun().
        /// Kteroukoli z metod může potomek overridovat.
        /// Bázová metoda vrací název obrázku z některé property this.ImageName???, odpovídající předanému stavu.
        /// </summary>
        /// <param name="moveResult"></param>
        /// <returns></returns>
        protected virtual string PineMoveGetImage(GameMoveResultType moveResult)
        {
            switch (moveResult)
            {
                case GameMoveResultType.None: return this.ImageNameOnMouse;
                case GameMoveResultType.OutOfCell: return this.ImageNameMoveOutOfCell;
                case GameMoveResultType.CellOk: return this.ImageNameMoveCellOk;
                case GameMoveResultType.CellWarning: return this.ImageNameMoveCellWarning;
                case GameMoveResultType.CellError: return this.ImageNameMoveCellError;
            }
            return this.ImageNameOnMouse;
        }
        /// <summary>
        /// Fyzicky dokončí pohyb figurky, pro daný bod, režim výsledku, buňku a vedlejší figurky na témže políčku.
        /// Metoda se volá poté, kdy uživatel táhl figurkou na nové místo, a figurku na něm uvolnil (upustil).
        /// Předcházející metoda (this.PineMoveEnd(), odkud se volá tato metoda) zjistí hodnotu moveResult, 
        /// najde cílové políčko (cell) kam uživatel figurku pustil (nebo zde bude null), 
        /// a najde figurky, které na daném políčku již stojí a předá je sem v parametru "pines"
        /// (pines může být null, když neexistuje políčko = když cell == null) (pines může být prázdné, když existuje cílové políčko a nejsou na něm figurky).
        /// Bázová metoda provede to, že v případech kdy "moveResult" je (CellOk nebo CellWarning) tak do this.Cell vloží políčko "cell", pro jiné hodnoty "moveResult" nikoliv.
        /// Pokud ne, pak se rozhodne pro animaci návratu na výchozí políčko (podle property this.AnimatedReturnEnable).
        /// Pokud nebude animace, pak uloží do this.ExplicitVisualBounds hodnotu null, tím zajistí to, že figurka nebude kreslena "na cestách" (na explicitní souřadnici), ale pěkně na svém domácím políčku.
        /// Na závěr nastaví obrázek figurky = ImageNameStandard.
        /// </summary>
        /// <param name="visualCenter"></param>
        /// <param name="moveResult"></param>
        /// <param name="cell"></param>
        /// <param name="pines"></param>
        /// <returns></returns>
        protected virtual bool PineMoveFinalize(PointF visualCenter, GameMoveResultType moveResult, GameCell cell, List<GamePine> pines)
        {
            bool runAnimation = false;
            switch (moveResult)
            {
                case GameMoveResultType.CellOk:
                case GameMoveResultType.CellWarning:
                    this.Cell = cell;
                    break;
                default:
                    runAnimation = this.AnimatedReturnEnable;
                    break;
            }
            if (runAnimation)
            {
                this.Owner.RunAnimation(this, this.PinAnimationGetPath(this.VisualCenter, this.Cell.VisualCenter));
            }
            else
            {
                this.ExplicitVisualBounds = null;                    // Zruším explicitní souřadnice => figurka bude na svém políčku.
                this.BackStdImageName = this.ImageNameStandard;      // Výchozí vzhled figurky
            }
            return true;
        }
        /// <summary>
        /// Metoda vygeneruje grafickou trasu pohybu animace figurky, z bodu 1 do bodu 2.
        /// Bázová metoda generuje přímou trasu s konstantní rychlostí 20 pixel / 15 milisec = 1333 pixel / sec.
        /// </summary>
        /// <param name="pointFrom"></param>
        /// <param name="pointTo"></param>
        /// <returns></returns>
        protected virtual IEnumerable<GameAnimatorPathPoint> PinAnimationGetPath(PointF pointFrom, PointF pointTo)
        {
            List<GameAnimatorPathPoint> path = new List<GameAnimatorPathPoint>();
            float fx = pointFrom.X;
            float fy = pointFrom.Y;
            float tx = pointTo.X;
            float ty = pointTo.Y;
            float dx = tx - fx;
            float dy = ty - fy;
            float length = (float)Math.Sqrt((double)(dx * dx + dy * dy));
            float step = 20f;         // Krok = 20 pixel na 15 milisekund
            float pos = step;
            while (pos < length)
            {
                float rel = pos / length;
                path.Add(new GameAnimatorPathPoint() { Point = new PointF(fx + rel * dx, fy + rel * dy), DelayAfter = 17 });
                pos += step;
            }
            return path;
        }
        /// <summary>
        /// Metoda volaná v průběhu animace.
        /// Nastavuje explicitní souřadnici středu figurky.
        /// </summary>
        /// <param name="visualCenter"></param>
        /// <returns></returns>
        protected virtual bool PineAnimationRun(PointF visualCenter)
        {
            this.VisualCenter = visualCenter;             // Zapíše potřebné hodnoty do this.ExplicitVisualBounds
            return true;
        }
        /// <summary>
        /// Metoda volaná při ukončení animace.
        /// </summary>
        /// <param name="visualCenter"></param>
        /// <returns></returns>
        protected virtual bool PineAnimationEnd(PointF visualCenter)
        {
            this.ExplicitVisualBounds = null;                    // Zruším explicitní souřadnice => figurka bude na svém políčku.
            this.BackStdImageName = this.ImageNameStandard;      // Výchozí vzhled figurky
            return true;
        }
        #endregion
        #region Override property
        /// <summary>
        /// Hrací figurky jsou ve vrstvě 2 (v klidu) anebo 3 (když se s nimi tahá)
        /// </summary>
        public override int VisualLayer { get { return (this.CurrentMouseState == MouseState.DragItem ? 3 : 2); } }
        /// <summary>
        /// Vizuální střed.
        /// Přebírá se z this.ExplicitVisualBounds, nebo z return this.Cell.VisualBounds.
        /// Ukládá se do base a do this.ExplicitVisualBounds.
        /// </summary>
        public override PointF VisualCenter
        {
            get
            {
                if (this.ExplicitVisualBounds.HasValue)
                    return this.ExplicitVisualBounds.Value.Center();
                if (this.Cell != null)
                    return this.Cell.VisualBounds.Center();
                return base.VisualCenter;
            }
            set
            {
                base.VisualCenter = value;
                this.ExplicitVisualBounds = value.FromCenter(this.VisualSize);
            }
        }
        /// <summary>
        /// Vizuální souřadnice.
        /// Přebírají se z this.ExplicitVisualBounds, nebo z return this.Cell.VisualBounds.
        /// Ukládají se do base, nikdy ne do this.ExplicitVisualBounds!
        /// </summary>
        public override RectangleF VisualBounds
        {
            get
            {
                if (this.ExplicitVisualBounds.HasValue)
                    return this.ExplicitVisualBounds.Value;
                if (this.Cell != null)
                    return this.Cell.VisualBounds.Center().FromCenter(this.VisualSize);
                return base.VisualBounds;
            }
            set
            {
                base.VisualBounds = value;
                //this.ExplicitVisualBounds = value;
            }
        }
        #endregion
    }
    #endregion
    #region class GameCell : Třída, která popisuje jedno políčko hrací plochy
    /// <summary>
    /// GameCell : Třída, která popisuje jednu buňku hrací plochy
    /// </summary>
    public class GameCell : GameItem
    {
        #region Konstrukce
        public GameCell()
        {
            this.DragEnable = false;
        }
        #endregion
        #region Override property
        /// <summary>
        /// Hrací políčka jsou ve vrstvě 1, vždy
        /// </summary>
        public override int VisualLayer { get { return 1; } }
        #endregion
    }
    #endregion
    #region class GameLink : Třída, která popisuje přechod z jedné buňky do druhé
    /// <summary>
    /// GameLink : Třída, která popisuje přechod z jedné buňky do druhé
    /// </summary>
    public class GameLink : IItem
    {
        #region Konstrukce
        /// <summary>
        /// GameLink : Třída, která popisuje přechod z jedné buňky do druhé.
        /// Konstruktor.
        /// </summary>
        public GameLink()
        {
            this.LogicalWidth = 1.5f;
            this.VisualWidth = 1.5f;
            this.LineColor = Color.DimGray;
        }
        #endregion
        #region Specifické property a metody
        /// <summary>
        /// Vlastník = hrací plocha.
        /// Skrze vlastníka může každé políčko dohledávat své sousedy.
        /// </summary>
        public IGame Owner { get; set; }
        /// <summary>
        /// Políčko 1, typicky předchozí, ale není to povinné pravidlo
        /// </summary>
        public GameCell Cell1 { get; set; }
        /// <summary>
        /// Políčko 2, typicky následující, ale není to povinné pravidlo
        /// </summary>
        public GameCell Cell2 { get; set; }
        /// <summary>
        /// Vrátí políčko podle směru: Next vrátí Cell2, Prev vrátí Cell1. Jinak vrací null.
        /// </summary>
        /// <param name="direction"></param>
        /// <returns></returns>
        public GameCell GetCell(GameMoveDirection direction)
        {
            switch (direction)
            {
                case GameMoveDirection.Next: return this.Cell2;
                case GameMoveDirection.Prev: return this.Cell1;
            }
            return null;
        }
        /// <summary>
        /// Barva pera. Implicitní je temně modrá (Color.DarkBlue).
        /// </summary>
        public virtual Color LineColor { get; set; }
        /// <summary>
        /// Šířka pera v logických souřadnicích, odpovídá logické velikosti ostatních prvků
        /// </summary>
        public virtual float LogicalWidth { get; set; }
        /// <summary>
        /// Šířka pera ve vizuálních souřadnicích, je přepočtena z logické šířky pera.
        /// </summary>
        public virtual float VisualWidth { get; set; }
        /// <summary>
        /// Příznak, zda se má kreslit Link.
        /// Aplikace může řídit.
        /// </summary>
        public virtual bool DrawLink { get; set; }
        /// <summary>
        /// Převede svoje logické souřadnice na fyzické, s použitém předaného transformačního objektu
        /// </summary>
        /// <param name="transformBound"></param>
        public void TransformLogicalToVisual(TransformBound transformBound)
        {
            this.VisualWidth = transformBound.TransformLogicalToVisual(this.LogicalWidth);
        }
        #endregion
        #region IItem Members
        int IItem.VisualLayer { get { return 0; } }
        PointF IItem.VisualCenter
        {
            get { return PointF.Empty; ; }
        }
        void IItem.Paint(GameArea gameArea, Graphics graphics)
        {
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            using (Pen pen = new Pen(this.LineColor, this.VisualWidth))
            {
                PointF point1 = this.Cell1.VisualCenter;
                PointF point2 = this.Cell2.VisualCenter;
                graphics.DrawLine(pen, point1, point2);
            }
        }
        bool IItem.IsOnPoint(PointF visualPoint)
        {
            // Link se nenachází na žádném bodu => nemůže být nikdy interaktivní:
            return false;
        }
        bool IItem.EnableAction(ActionType action)
        {
            switch (action)
            {
                case ActionType.Paint:
                    // Pozor, v tuhle chvíli ještě nemusí být platné souřadnice Cell.VisualBounds !!!
                    // Kreslit se Link bude tehdy, pokud zná obě políčka (Cell1 i Cell2):
                    return (this.DrawLink && this.Cell1 != null && this.Cell2 != null);
            }
            return false;
        }
        bool IItem.RunAction(ActionType action, PointF visualCenter)
        {
            return false;
        }
        #endregion
    }
    #endregion
    #region class GameItem : Třída, která je předkem interaktivních grafických prvků
    /// <summary>
    /// GameItem : Třída, která je předkem interaktivních grafických prvků (GameCell, GamePine)
    /// </summary>
    public class GameItem : IItem
    {
        #region Konstrukce
        public GameItem()
        {
            this.CurrentMouseState = MouseState.None;
            this.OnHotBoundsExpandRatio = 0f;
            this.AnimatedReturnEnable = false;
        }
        #endregion
        #region Vlastník
        /// <summary>
        /// Vlastník = hrací plocha.
        /// Skrze vlastníka může každé políčko dohledávat své sousedy.
        /// </summary>
        public Game Owner { get; set; }
        #endregion
        #region Logické a vizuální souřadnice (Center, Size, Bounds)
        /// <summary>
        /// Souřadnice středu políčka, v logických souřadnicích
        /// </summary>
        public virtual PointF LogicalCenter
        {
            get { return this._LogicalCenter; }
            set { this._LogicalCenter = value; this._SetLogicalDimensions(); }
        }
        /// <summary>
        /// Rozměr políčka, v logických souřadnicích
        /// </summary>
        public virtual SizeF LogicalSize
        {
            get { return this._LogicalSize; }
            set { this._LogicalSize = value; this._SetLogicalDimensions(); }
        }
        /// <summary>
        /// Aktuální logické souřadnice buňky (dané hodnotami this.LogicalCenter a this.LogicalSize)
        /// </summary>
        public virtual RectangleF LogicalBounds
        {
            get { return this._LogicalBounds; }
            set { this._LogicalBounds = value; this._SetLogicalBound(); }
        }
        /// <summary>
        /// Souřadnice středu políčka, v vizuálních souřadnicích
        /// </summary>
        public virtual PointF VisualCenter
        {
            get { return this._VisualCenter; }
            set { this._VisualCenter = value; this._SetVisualDimensions(); }
        }
        /// <summary>
        /// Rozměr políčka, v vizuálních souřadnicích
        /// </summary>
        public virtual SizeF VisualSize
        {
            get { return this._VisualSize; }
            set { this._VisualSize = value; this._SetVisualDimensions(); }
        }
        /// <summary>
        /// Aktuální vizuální souřadnice buňky (dané hodnotami this.VisualCenter a this.VisualSize)
        /// </summary>
        public virtual RectangleF VisualBounds
        {
            get { return this._VisualBounds; }
            set { this._VisualBounds = value; this._SetVisualBound(); }
        }
        /// <summary>
        /// Převede svoje logické souřadnice na fyzické, s použitém předaného transformačního objektu
        /// </summary>
        /// <param name="transformBound"></param>
        public void TransformLogicalToVisual(TransformBound transformBound)
        {
            this.VisualBounds = transformBound.TransformLogicalToVisual(this.LogicalBounds);
        }
        private void _SetLogicalDimensions()
        {
            this._LogicalBounds = this._LogicalCenter.FromCenter(this._LogicalSize);
        }
        private void _SetLogicalBound()
        {
            this._LogicalCenter = this._LogicalBounds.Center();
            this._LogicalSize = this._LogicalBounds.Size;
        }
        private void _SetVisualDimensions()
        {
            this._VisualBounds = this._VisualCenter.FromCenter(this._VisualSize);
        }
        private void _SetVisualBound()
        {
            this._VisualCenter = this._VisualBounds.Center();
            this._VisualSize = this._VisualBounds.Size;
        }
        private PointF _LogicalCenter;
        private SizeF _LogicalSize;
        private RectangleF _LogicalBounds;
        private PointF _VisualCenter;
        private SizeF _VisualSize;
        private RectangleF _VisualBounds;
        #endregion
        #region Grafické vlastnosti, podpora pro kreslení, vlastní kreslení
        /// <summary>
        /// Vykreslí tento prvek.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void Paint(GameArea gameArea, Graphics graphics)
        {
            // Základní pozadí: obrázek nebo barva, pro stav Std nebo Hot:
            bool isHot = this.CurrentIsHot;
            RectangleF bounds = this.CurrentHotVisualBounds;
            Image image = this.CurrentBackImage;
            if (image != null)
                graphics.DrawImage(image, bounds);
            else
            {
                Color backColor = (!isHot ? this.BackStdColor : this.BackHotColor);
                using (Brush sb = new SolidBrush(backColor))
                {
                    if (this.BackShapeEllipse)
                        graphics.FillEllipse(sb, bounds);
                    else
                        graphics.FillRectangle(sb, bounds);
                }
            }

            // Obrázek přes vrch:
            image = this.CurrentForeImage;
            if (image != null)
                graphics.DrawImage(image, bounds);
        }
        /// <summary>
        /// Barva pozadí v případě, kdy není k dispozici obrázek BackStdImageName.
        /// Pro políčko ve stavu bez myši (není Hot).
        /// </summary>
        public virtual Color BackStdColor { get; set; }
        /// <summary>
        /// Barva pozadí v případě, kdy není k dispozici obrázek BackHotImageName.
        /// Pro políčko ve stavu s myší (je Hot).
        /// </summary>
        public virtual Color BackHotColor { get; set; }
        /// <summary>
        /// Druh tvaru, který se kreslí jako pozadí v případě, když neexistuje Image.
        /// false = default = rectangle, true = elipsa
        /// </summary>
        public virtual bool BackShapeEllipse { get; set; }
        /// <summary>
        /// Obrázek definující prázdné políčko, podklad, bez ozdob.
        /// Pro políčko ve stavu bez myši (není Hot).
        /// </summary>
        public virtual string BackStdImageName { get; set; }
        /// <summary>
        /// Obrázek definující prázdné políčko, podklad, bez ozdob.
        /// Pro políčko ve stavu s myší (je Hot).
        /// </summary>
        public virtual string BackHotImageName { get; set; }
        /// <summary>
        /// Obrázek definující popředí políčka.
        /// Kreslí se nad pozadí, a to jen tehdy pokud toto jméno obrázku není prázdné.
        /// Pro políčko ve stavu bez myši (není Hot).
        /// </summary>
        public virtual string ForeStdImageName { get; set; }
        /// <summary>
        /// Obrázek definující popředí políčka.
        /// Kreslí se nad pozadí, a to jen tehdy pokud toto jméno obrázku není prázdné.
        /// Pro políčko ve stavu s myší (je Hot).
        /// </summary>
        public virtual string ForeHotImageName { get; set; }
        /// <summary>
        /// Hodnota, o kterou se změní rozměr this.VisualBound za stavu this.CurrentIsHot.
        /// </summary>
        public virtual float OnHotBoundsExpandRatio { get; set; }
        #endregion
        #region Interaktivní vlastnosti a metody
        /// <summary>
        /// Číslo políčka / figurky
        /// </summary>
        public virtual int Index { get; protected set; }
        /// <summary>
        /// Hráč, jehož je tento prvek (políčko, figurka)
        /// </summary>
        public virtual GamePlayer Player { get; protected set; }
        /// <summary>
        /// Aktuální číslo vrstvy, do které se prvek kreslí.
        /// GameArea používá 4 vrstvy: 0=pozadí a statické prvky, 1=políčka, 2=figurky na políčku, 3=figurky pohybované myší.
        /// GameArea v rámci interaktivity určuje, kterých vrstev se změna prováděná uživatelem týká, následně volá ReDraw() pro tyto vrstvy.
        /// GameDefinition sestaví soupisy položek pro kreslení podle jejich vrstvy (tato property), a zajistí korektní vykreslování jednotlivých vrstev.
        /// </summary>
        public virtual int VisualLayer { get { return 1; } }
        /// <summary>
        /// Vrátí informaci o tom, zda tento prvek se nachází na dané souřadnici
        /// </summary>
        /// <param name="visualPoint"></param>
        /// <returns></returns>
        public virtual bool IsOnPoint(PointF visualPoint)
        {
            return this.VisualBounds.Contains(visualPoint);
        }
        /// <summary>
        /// Vrátí informaci, zda daný prvek podporuje danou akci.
        /// Base třída povoluje Paint, MouseEnter a MouseLeave bez podmínky,
        /// a podle podmínky DragEnable povoluje MoveBegin, MoveEnd, MouseLeave.
        /// </summary>
        /// <param name="action"></param>
        /// <returns></returns>
        protected virtual bool EnableAction(ActionType action)
        {
            switch (action)
            {
                case ActionType.MouseEnter: return true;
                case ActionType.Paint: return true;
                case ActionType.MoveBegin: return this.DragEnable;
                case ActionType.MoveRun: return this.DragEnable;
                case ActionType.MoveEnd: return this.DragEnable;
                case ActionType.MouseLeave: return true;
            }
            return false;
        }
        /// <summary>
        /// Provede danou akci. Vrací informaci, zda je následně třeba překreslit plochu.
        /// Base třída řeší: this.CurrentMouseState, this.DragEnable, this.VisualCenter.
        /// </summary>
        /// <param name="action"></param>
        /// <param name="visualCenter"></param>
        /// <returns></returns>
        protected virtual bool RunAction(ActionType action, PointF visualCenter)
        {
            bool needDraw = false;
            switch (action)
            {
                case ActionType.MouseEnter:
                    this.CurrentMouseState = MouseState.OnControl;
                    needDraw = true;
                    break;
                case ActionType.Paint:
                    needDraw = true;
                    break;
                case ActionType.MoveBegin:
                    if (this.DragEnable)
                    {
                        this.CurrentMouseState = MouseState.DragItem;
                        needDraw = true;
                    }
                    break;
                case ActionType.MoveRun:
                    if (this.DragEnable)
                    {
                        this.VisualCenter = visualCenter;
                        needDraw = true;
                    }
                    break;
                case ActionType.MoveEnd:
                    if (this.DragEnable)
                    {
                        this.CurrentMouseState = MouseState.OnControl;
                        needDraw = true;
                    }
                    break;
                case ActionType.MouseLeave:
                    this.CurrentMouseState = MouseState.None;
                    needDraw = true;
                    break;
            }
            return needDraw;
        }
        /// <summary>
        /// Příznak (true), když aktuální stav myši odpovídá grafickému stavu IsHot (prvek je aktivní)
        /// </summary>
        public virtual bool CurrentIsHot
        {
            get
            {
                MouseState s = this.CurrentMouseState;
                return (s == MouseState.OnControl ||
                        s == MouseState.LeftDown ||
                        s == MouseState.DragItem);
            }
        }
        /// <summary>
        /// Aktuální rozměr včetně zohlednění stavu this.CurrentIsHot a odpovídajícího koeficientu this.OnHotBoundsExpandRatio
        /// </summary>
        protected virtual RectangleF CurrentHotVisualBounds
        {
            get
            {
                RectangleF bounds = this.VisualBounds;
                bool isHot = this.CurrentIsHot;
                float expRatio = this.OnHotBoundsExpandRatio;
                if (isHot && expRatio != 0f)
                    return bounds.Expand(expRatio);
                return bounds;
            }
        }
        /// <summary>
        /// Obsahuje (vrátí) obrázek BackImage (BackStdImageName nebo BackHotImageName), podle stavu this.CurrentIsHot
        /// </summary>
        protected virtual Image CurrentBackImage
        {
            get
            {
                return this.GetImage(this.BackStdImageName, this.BackHotImageName);
            }
        }
        /// <summary>
        /// Obsahuje (vrátí) obrázek ForeImage (ForeStdImageName nebo ForeHotImageName), podle stavu this.CurrentIsHot
        /// </summary>
        protected virtual Image CurrentForeImage
        {
            get
            {
                return this.GetImage(this.ForeStdImageName, this.ForeHotImageName);
            }
        }
        /// <summary>
        /// Vrátí obrázek pro aktuální stav this.CurrentIsHot, pro jedno ze dvou zadaných jmen.
        /// </summary>
        /// <param name="imageNameStd"></param>
        /// <param name="imageNameHot"></param>
        /// <returns></returns>
        protected Image GetImage(string imageNameStd, string imageNameHot)
        {
            Image image = null;
            if (this.CurrentIsHot && !String.IsNullOrEmpty(imageNameHot) && this.Owner.ImageLibrary.ContainImage(imageNameHot))
                image = this.Owner.ImageLibrary[imageNameHot];
            if (image == null && !String.IsNullOrEmpty(imageNameStd) && this.Owner.ImageLibrary.ContainImage(imageNameStd))
                image = this.Owner.ImageLibrary[imageNameStd];
            return image;
        }
        /// <summary>
        /// Příznak (true), když aktuální prvek je možno přesouvat na jiné místo myší.
        /// </summary>
        protected virtual bool DragEnable { get; set; }
        /// <summary>
        /// Příznak (true), když aktuální prvek chce provádět návrat z Error cell na výchozí souřadnici pomocí animace.
        /// </summary>
        protected virtual bool AnimatedReturnEnable { get; set; }
        /// <summary>
        /// Aktuální stav myši na tomto objektu, má vliv na jeho vykreslování
        /// </summary>
        public virtual MouseState CurrentMouseState
        {
            get { return this._CurrentMouseState; }
            protected set { this._CurrentMouseState = value; } 
        }
        private MouseState _CurrentMouseState;
        #endregion
        #region IItem Members
        int IItem.VisualLayer { get { return this.VisualLayer; } }
        PointF IItem.VisualCenter { get { return this.VisualCenter; } }
        void IItem.Paint(GameArea gameArea, Graphics graphics)
        {
            this.Paint(gameArea, graphics);
        }
        bool IItem.IsOnPoint(PointF visualPoint)
        {
            return this.IsOnPoint(visualPoint);
        }
        bool IItem.EnableAction(ActionType action)
        {
            return this.EnableAction(action);
        }
        bool IItem.RunAction(ActionType action, PointF visualCenter)
        {
            return this.RunAction(action, visualCenter);
        }
        #endregion
    }
    #endregion
    #region class TransformBound : třída, která provádí transformaci souřadnic logických na vizuální a naopak
    /// <summary>
    /// TransformBound : třída, která provádí transformaci souřadnic logických na vizuální a naopak
    /// </summary>
    public class TransformBound
    {
        #region Konstrukce a proměnné
        public TransformBound(RectangleF logicalArea, RectangleF visualArea)
        {
            this.LogicalArea = logicalArea;
            this.VisualArea = visualArea;
            this._CalculateTransform(logicalArea, visualArea);
        }
        private void _CalculateTransform(RectangleF logicalArea, RectangleF visualArea)
        {
            this._TransformCf = 1f;
            this._TransformDx = 0f;
            this._TransformDy = 0f;
            if (logicalArea.Width > 0f && logicalArea.Height > 0f && visualArea.Width > 0f && visualArea.Height > 0f)
            {
                this._TransformCf = Math.Min(visualArea.Width / logicalArea.Width, visualArea.Height / logicalArea.Height);
                this._TransformDx = TransformPrepareOffset(visualArea.X, visualArea.Width, logicalArea.X, logicalArea.Width);
                this._TransformDy = TransformPrepareOffset(visualArea.Y, visualArea.Height, logicalArea.Y, logicalArea.Height);
                this._TransformValid = true;
            }
        }
        /// <summary>
        /// Určí hodnotu ofsetu, který bude použit k posunutí logické souřadnice do souřadnice vizuální.
        /// </summary>
        /// <param name="visBegin"></param>
        /// <param name="visLength"></param>
        /// <param name="logBegin"></param>
        /// <param name="logLength"></param>
        /// <param name="coefficient"></param>
        /// <returns></returns>
        protected float TransformPrepareOffset(float visBegin, float visLength, float logBegin, float logLength)
        {
            //  Na kterém pixelu bude začínat kresba logických dat:
            // 1) Vezmeme logickou délku (např. šířka) * koeficient = délka v pixelech, vydělíme 2 = vizuální hodnota poloviny velikosti
            // 2) Vezmeme vizuální střed (například X + 1/2 šířky), protože chceme logický prostor vycentrovat ve vizuálním prostoru
            // 3) Od vizuálního středu odečteme polovinu vizuální velikosti => a máme souřadnici, kde bude vykreslován první bod.
            float vis1 = visBegin + 0.5f * visLength - 0.5f * this._TransformCf * logLength;

            //  Výpočet vizuálního offsetu (tj. hodnota, která se přičte k logické souřadnici vynásobené koeficientem, a výsledek dá vizuální souřadnici):
            // 1) Určíme vizuální pozici (pixel), na který by se kreslila logická souřadnice 0.00 = bod, vzdálený o (coefficient * logBegin) od bodi vis1.
            float vis0 = vis1 - this._TransformCf * logBegin;

            // To je vše:
            return vis0;
        }
        private RectangleF? _TransformLastVisual;
        private float _TransformCf;
        private float _TransformDx;
        private float _TransformDy;
        private bool _TransformValid;
        #endregion
        #region Public property
        public RectangleF LogicalArea { get; private set; }
        public RectangleF VisualArea { get; private set; }
        #endregion
        #region Public metody pro transformace
        /// <summary>
        /// Vypočte a vrátí vizuální souřadnice pro dané logické souřadnice a aktuální transformační parametry.
        /// </summary>
        /// <param name="logicalBound">logické souřadnice</param>
        /// <returns></returns>
        public RectangleF TransformLogicalToVisual(RectangleF logicalBound)
        {
            if (!this._TransformValid) return logicalBound;
            return new RectangleF(
                this.TransformLogicalToVisual(logicalBound.Location),
                this.TransformLogicalToVisual(logicalBound.Size));
        }
        /// <summary>
        /// Vypočte a vrátí vizuální bod pro daný logický bod a aktuální transformační parametry.
        /// </summary>
        /// <param name="logicalBound">logický bod</param>
        /// <returns></returns>
        public PointF TransformLogicalToVisual(PointF logicalPoint)
        {
            if (!this._TransformValid) return logicalPoint;
            return new PointF(logicalPoint.X * this._TransformCf + this._TransformDx,
                              logicalPoint.Y * this._TransformCf + this._TransformDy);
        }
        /// <summary>
        /// Vypočte a vrátí vizuální velikost pro danou logickou velikost a aktuální transformační parametry.
        /// </summary>
        /// <param name="logicalSize">logická velikost</param>
        /// <returns></returns>
        public SizeF TransformLogicalToVisual(SizeF logicalSize)
        {
            if (!this._TransformValid) return logicalSize;
            return new SizeF(logicalSize.Width * this._TransformCf,
                             logicalSize.Height * this._TransformCf);
        }
        /// <summary>
        /// Vypočte a vrátí vizuální délku pro danou logickou délku a aktuální transformační parametry.
        /// </summary>
        /// <param name="logicalSize">logická velikost</param>
        /// <returns></returns>
        public float TransformLogicalToVisual(float logicalLength)
        {
            if (!this._TransformValid) return logicalLength;
            return logicalLength * this._TransformCf;
        }
        /// <summary>
        /// Vypočte a vrátí logické souřadnice pro dané vizuální souřadnice a aktuální transformační parametry.
        /// </summary>
        /// <param name="visualBound">logické souřadnice</param>
        /// <returns></returns>
        public RectangleF TransformVisualToLogical(RectangleF visualBound)
        {
            if (!this._TransformValid) return visualBound;
            return new RectangleF(
                this.TransformVisualToLogical(visualBound.Location),
                this.TransformVisualToLogical(visualBound.Size));
        }
        /// <summary>
        /// Vypočte a vrátí logický bod pro daný vizuální bod a aktuální transformační parametry.
        /// </summary>
        /// <param name="logicalBound">logický bod</param>
        /// <returns></returns>
        public PointF TransformVisualToLogical(PointF visualPoint)
        {
            if (!this._TransformValid) return visualPoint;
            return new PointF((visualPoint.X - this._TransformDx) / this._TransformCf,
                              (visualPoint.Y - this._TransformDy) / this._TransformCf);
        }
        /// <summary>
        /// Vypočte a vrátí logickou velikost pro danou vizuální velikost a aktuální transformační parametry.
        /// </summary>
        /// <param name="visualSize">logická velikost</param>
        /// <returns></returns>
        public SizeF TransformVisualToLogical(SizeF visualSize)
        {
            if (!this._TransformValid) return visualSize;
            return new SizeF(visualSize.Width / this._TransformCf,
                             visualSize.Height / this._TransformCf);
        }
        /// <summary>
        /// Vypočte a vrátí logickou délku pro danou vizuální délku a aktuální transformační parametry.
        /// </summary>
        /// <param name="visualSize">logická velikost</param>
        /// <returns></returns>
        public float TransformVisualToLogical(float visualLength)
        {
            if (!this._TransformValid) return visualLength;
            return visualLength / this._TransformCf;
        }
        #endregion
    }
    #endregion
    #region enumy : GameMoveDirection, GameMoveResultType
    /// <summary>
    /// Směry pohybu mezi políčky na základě vztahů GameLink, viz metoda GameDefinition.FindLinkOnCell()
    /// </summary>
    public enum GameMoveDirection
    {
        None = 0,
        /// <summary>
        /// Hledáme políčka Next = v objektu Linku vycházíme z Cell1 (abychom se dostali na Cell2 == Next)
        /// </summary>
        Next,
        /// <summary>
        /// Hledáme políčka Prev = v objektu Linku vycházíme z Cell2 (abychom se dostali na Cell1 == Prev)
        /// </summary>
        Prev
    }
    /// <summary>
    /// Stav / výsledek pohybu figurky po ploše
    /// </summary>
    public enum GameMoveResultType
    {
        /// <summary>Neurčeno</summary>
        None = 0,
        /// <summary>Mimo políčko</summary>
        OutOfCell,
        /// <summary>Na políčku, kam mohu bez následků vstoupit</summary>
        CellOk,
        /// <summary>Na políčku, kam mohu vstoupit s nějakými následky (podrobnosti řeší potomstvo)</summary>
        CellWarning,
        /// <summary>Na políčku, kam nemohu vstoupit</summary>
        CellError
    }
    #endregion
}
